package web

import (
	"booksmart/book/internal/domain"
	"booksmart/book/internal/service"
	"booksmart/book/pkg/ginx"
	logger2 "booksmart/book/pkg/logger"
	"errors"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"net/http"
)

const biz = "login"

var (
	ErrCodeSentTooMany   = service.ErrCodeSentTooMany
	ErrCodeVerifyTooMany = service.ErrCodeVerifyTooMany
)

type UserHandler struct {
	svc     *service.UserService
	codeSvc *service.CodeService
	l       logger2.LoggerV1
}

func NewUserHandler(svc *service.UserService, codeSvc *service.CodeService, l logger2.LoggerV1) *UserHandler {
	return &UserHandler{svc: svc, codeSvc: codeSvc, l: l}
}

func (u *UserHandler) RegisterRoutes(server *gin.Engine) {
	ginx.L = u.l
	group := server.Group("users")
	group.POST("/login", ginx.WrapBody[LoginReq](u.Login))
	group.POST("/edit", u.Edit)
	group.POST("/profile", ginx.WrapBodyAndToken[LoginReq, domain.Author](u.Profile))
	group.POST("/singUp", u.SingUp)
}

func (u *UserHandler) SendSmsMessage(ctx *gin.Context) {
	type SendReq struct {
		Phone string `json:"phone"`
	}
	var req SendReq
	err := ctx.Bind(&req)
	if err != nil {
		return
	}
	err = u.codeSvc.Send(ctx, biz, req.Phone)
	switch {
	case err == nil:
		ctx.JSON(http.StatusOK, Status{
			Code: 5,
			Msg:  "发送成功",
		})
	case errors.Is(err, ErrCodeSentTooMany):
		ctx.JSON(http.StatusOK, Status{
			Code: 5,
			Msg:  "发送太频繁,请稍后再试",
		})
	default:
		ctx.JSON(http.StatusOK, Status{
			Code: 5,
			Msg:  "系统错误",
		})
	}

}

func (u *UserHandler) smsVerify(ctx *gin.Context) {
	type SendReq struct {
		Phone string `json:"phone"`
		Code  string `json:"code"`
	}
	var req SendReq
	err := ctx.Bind(&req)
	if err != nil {
		return
	}
	ok, err := u.codeSvc.Verify(ctx, biz, req.Phone, req.Code)
	if err != nil {
		ctx.JSON(http.StatusOK, Status{
			Code: 5,
			Msg:  "系统错误",
		})
		u.l.Error("校验验证码出错", logger2.Error(err))
		return
	}
	if !ok {
		ctx.JSON(http.StatusOK, Status{
			Code: 4,
			Msg:  "验证码有误",
		})
		return
	}

	user, err := u.svc.FindOrCreate(ctx, req.Phone)
	if err != nil {
		ctx.JSON(http.StatusOK, Status{
			Code: 5,
			Msg:  "系统错误",
		})
		return
	}
	err = u.setToken(ctx, user)
	if err != nil {
		ctx.JSON(http.StatusOK, Status{
			Code: 5,
			Msg:  "系统错误",
		})
		return
	}
	ctx.JSON(http.StatusOK, Status{
		Code: 4,
		Msg:  "验证码通过",
	})

}

type LoginReq struct {
	Email    string `form:"email"`
	Password string `form:"password"`
}

func (u *UserHandler) Login(ctx *gin.Context, loginReq LoginReq) (ginx.Status, error) {
	user, err := u.svc.Login(ctx, domain.User{
		Email:    loginReq.Email,
		Password: loginReq.Password,
	})
	if errors.Is(err, service.ErrInvalidUserOrPassword) {
		//ctx.String(http.StatusOK, "密码或邮箱不对")
		return Status{Code: 5, Msg: "密码或邮箱不对"}, ErrCodeVerifyTooMany
	}
	if err != nil {
		//ctx.String(http.StatusOK, "系统错误")
		return Status{Code: 5, Msg: "系统错误"}, err
	}
	if err = u.setToken(ctx, user); err != nil {
		//ctx.String(http.StatusOK, "系统错误")
		return Status{Code: 5, Msg: "系统错误"}, err
	}
	//ctx.String(http.StatusOK, "登录成功")
	return Status{Msg: "登录成功"}, nil

}

func (u *UserHandler) setToken(ctx *gin.Context, user domain.User) error {
	session := sessions.Default(ctx)
	session.Set("userId", user.Id)
	session.Options(sessions.Options{
		MaxAge: 60 * 10,
	})
	err := session.Save()
	if err != nil {
		zap.L().Error("设置 session 出现异常", zap.Error(err))
		return err
	}
	return nil
}

func (u *UserHandler) Edit(ctx *gin.Context) {

}

func (u *UserHandler) Profile(ctx *gin.Context, req LoginReq, authorInfo domain.Author) (ginx.Status, error) {
	return Status{Msg: "系統錯誤", Data: authorInfo.Id}, errors.New("拋出異常")
	ctx.String(http.StatusOK, "这里是profile")
	return Status{Msg: "OK", Data: authorInfo.Id}, nil
}

func (u *UserHandler) SingUp(ctx *gin.Context) {
	type SingUpReq struct {
		Email           string `json:"email"`
		ConfirmPassword string `json:"confirmPassword"`
		Password        string `json:"password"`
	}
	var req SingUpReq
	if err := ctx.Bind(&req); err != nil {
		return
	}

	if req.ConfirmPassword != req.Password {
		ctx.String(http.StatusOK, "两次输入的密码不一致")
		return
	}
	err := u.svc.SingUp(ctx, domain.User{
		Email:    req.Email,
		Password: req.Password,
	})
	if errors.Is(err, service.ErrUserDuplicate) {
		ctx.String(http.StatusOK, "邮箱已存在")
		return
	}
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}
	ctx.String(http.StatusOK, "登录成功")
	return
}
